{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f# Here we are builing an Image Classfier using Keras. The dataset used is from Kaggle Dog vs Cat Image \n",
    "# Classification Problem. First let's build the dataset\n",
    "# Step 1: Download the dataset from Kaggle\n",
    "# Step 2: Unzip the dataset\n",
    "# Step 3: You will find 2 folder, test and train\n",
    "# Step 4: Delete the test folder. We will create our own test folder.\n",
    "# Step 5: Inside both the train and test folders, create 2 sub-folders cats and dogs.\n",
    "# Step 7: Put all the cat's image in cats folder and all the dog's image in dogs folder\n",
    "# Step 8: Take some images (I took 2000) from train>cats folder and put in test>cats folder\n",
    "# Step 9: Take some images (I took 2000) from train>dogs folder and put in test>dogs folder\n",
    "# Step 10: Your dataset is ready"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import Sequential# Import the sequential layer. \n",
    "#Generally there are two types of layers, sequential and functional. Sequential is most common one\n",
    "from keras.layers import Conv2D,Activation,MaxPooling2D,Dense,Flatten,Dropout\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Initialize a catDogImageclassifier variable here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier = Sequential()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We are adding layers to our network here.\n",
    "# Conv2D: 2 dimensional convolutional layer\n",
    "# 32: filters required. \n",
    "# 3,3: size of the filter (3 rows, 3 columns)\n",
    "# Input Image shape is 64*64*3 - height*width*RGB. Each number represents pixel intensity (0-255)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Conv2D(32,(3,3),input_shape=(64,64,3)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Output is a feature map. The training data will work on it and get some feature maps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Lets add the activation function now. We are using ReLU (Rectified Linear Unit). \n",
    "#The activation function gives the output basis the output. In the feature map output from the previous layer,\n",
    "#the activation function will replace all the negative pixels with zero"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Activation('relu'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We do not want out network to be overly complex computationally, hence the pooling layer comes into picture\n",
    "# The pooling layer will reduce the dimensions. Max with two by two filter, will take the maximum value but the \n",
    "# significant features will be retained"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(MaxPooling2D(pool_size =(2,2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# All three convolutional blocks. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Conv2D(32,(3,3))) # Convolutional Layer\n",
    "catDogImageclassifier.add(Activation('relu')) # Activation Layer\n",
    "catDogImageclassifier.add(MaxPooling2D(pool_size =(2,2))) # Pooling Layer\n",
    "catDogImageclassifier.add(Conv2D(32,(3,3))) # Convolutional Layer\n",
    "catDogImageclassifier.add(Activation('relu')) # Activation Layer\n",
    "catDogImageclassifier.add(MaxPooling2D(pool_size =(2,2))) # Pooling Layer\n",
    "catDogImageclassifier.add(Conv2D(32,(3,3))) # Convolutional Layer\n",
    "catDogImageclassifier.add(Activation('relu')) # Activation Layer\n",
    "catDogImageclassifier.add(MaxPooling2D(pool_size =(2,2))) # Pooling Layer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Overfitting is a nuicance. We have to fight it using Drop out. Prepare the data by flatenning it. \n",
    "#And flattening to 1 dimension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Flatten())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add dense function now followed by ReLU activation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Dense(64))\n",
    "catDogImageclassifier.add(Activation('relu'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Here add the doropout layer\n",
    "# Overfitting means that model is working good for training but failing on testing dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Dropout(0.5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add one more fully connected layer now to get the output in n-dimensional classes (a vector will be the output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Dense(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Sigmoid function to convert to probabilities"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.add(Activation('sigmoid'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let us look how out network looks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_27 (Conv2D)           (None, 62, 62, 32)        896       \n",
      "_________________________________________________________________\n",
      "activation_22 (Activation)   (None, 62, 62, 32)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_20 (MaxPooling (None, 31, 31, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_28 (Conv2D)           (None, 29, 29, 32)        9248      \n",
      "_________________________________________________________________\n",
      "activation_23 (Activation)   (None, 29, 29, 32)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_21 (MaxPooling (None, 14, 14, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_29 (Conv2D)           (None, 12, 12, 32)        9248      \n",
      "_________________________________________________________________\n",
      "activation_24 (Activation)   (None, 12, 12, 32)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_22 (MaxPooling (None, 6, 6, 32)          0         \n",
      "_________________________________________________________________\n",
      "conv2d_30 (Conv2D)           (None, 4, 4, 32)          9248      \n",
      "_________________________________________________________________\n",
      "activation_25 (Activation)   (None, 4, 4, 32)          0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_23 (MaxPooling (None, 2, 2, 32)          0         \n",
      "_________________________________________________________________\n",
      "flatten_2 (Flatten)          (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 64)                8256      \n",
      "_________________________________________________________________\n",
      "activation_26 (Activation)   (None, 64)                0         \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 64)                0         \n",
      "_________________________________________________________________\n",
      "dense_4 (Dense)              (None, 1)                 65        \n",
      "_________________________________________________________________\n",
      "activation_27 (Activation)   (None, 1)                 0         \n",
      "=================================================================\n",
      "Total params: 36,961\n",
      "Trainable params: 36,961\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "catDogImageclassifier.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# A quick look at the network summary states that total number of parameters in our network are 36,961. \n",
    "#Play around with different network structures and have a look how this number changes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.compile(optimizer ='rmsprop',# rmsprop is the optimizer using Gradient Descent\n",
    "                   loss ='binary_crossentropy', # Loss or cost function for the model\n",
    "                   metrics =['accuracy']) # The KPI "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let us do some data augmentation here. It helps to fight overfitting. Zoom, scale etc. \n",
    "# There is a function ImageDataGenerator which is used here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "train_datagen = ImageDataGenerator(rescale =1./255,\n",
    "                                   shear_range =0.25,\n",
    "                                   zoom_range = 0.25,\n",
    "                                   horizontal_flip =True)\n",
    "test_datagen = ImageDataGenerator(rescale = 1./255)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the training data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 23000 images belonging to 2 classes.\n"
     ]
    }
   ],
   "source": [
    "training_set = train_datagen.flow_from_directory('/Users/vaibhavverdhan/Book Writing/DogsCats/train',target_size=(64,64),batch_size= 32,class_mode='binary')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the testing data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 2000 images belonging to 2 classes.\n"
     ]
    }
   ],
   "source": [
    "test_set = test_datagen.flow_from_directory('/Users/vaibhavverdhan/Book Writing/DogsCats/test',\n",
    "                                           target_size = (64,64),\n",
    "                                           batch_size = 32,\n",
    "                                           class_mode ='binary')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let us begin the training now. Steps per epoch is 625 and number of epochs is 10. \n",
    "#Epoch is one full cycle of the training data\n",
    "# Steps and Batch size has to be understood next. For example: if we have 1000 images and batch size of 10, it means\n",
    "# number of steps = 1000/10 which is 100 steps required.\n",
    "# Depending on the complexity of the network, the number of epochs given etc., the compilation will take time.\n",
    "# The test dataset is passed as a validation_data here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "625/625 [==============================] - 185s 296ms/step - loss: 0.6721 - acc: 0.5822 - val_loss: 0.6069 - val_acc: 0.6610\n",
      "Epoch 2/10\n",
      "625/625 [==============================] - 152s 243ms/step - loss: 0.5960 - acc: 0.6831 - val_loss: 0.5151 - val_acc: 0.7543\n",
      "Epoch 3/10\n",
      "625/625 [==============================] - 151s 242ms/step - loss: 0.5452 - acc: 0.7217 - val_loss: 0.4891 - val_acc: 0.7545\n",
      "Epoch 4/10\n",
      "625/625 [==============================] - 150s 239ms/step - loss: 0.5069 - acc: 0.7568 - val_loss: 0.4657 - val_acc: 0.7743\n",
      "Epoch 5/10\n",
      "625/625 [==============================] - 150s 240ms/step - loss: 0.4813 - acc: 0.7713 - val_loss: 0.4407 - val_acc: 0.7925\n",
      "Epoch 6/10\n",
      "625/625 [==============================] - 152s 243ms/step - loss: 0.4526 - acc: 0.7866 - val_loss: 0.4374 - val_acc: 0.7924\n",
      "Epoch 7/10\n",
      "625/625 [==============================] - 151s 241ms/step - loss: 0.4458 - acc: 0.7953 - val_loss: 0.3891 - val_acc: 0.8324\n",
      "Epoch 8/10\n",
      "625/625 [==============================] - 151s 242ms/step - loss: 0.4177 - acc: 0.8123 - val_loss: 0.3917 - val_acc: 0.8221\n",
      "Epoch 9/10\n",
      "625/625 [==============================] - 155s 248ms/step - loss: 0.4158 - acc: 0.8158 - val_loss: 0.3947 - val_acc: 0.8176\n",
      "Epoch 10/10\n",
      "625/625 [==============================] - 151s 241ms/step - loss: 0.4021 - acc: 0.8201 - val_loss: 0.3783 - val_acc: 0.8221\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.callbacks.History at 0x1a376b5160>"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import display\n",
    "from PIL import Image\n",
    "catDogImageclassifier.fit_generator(training_set,\n",
    "                        steps_per_epoch =625,\n",
    "                        epochs = 10,\n",
    "                        validation_data =test_set,\n",
    "                        validation_steps = 1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can see here that in the final epoch we got validation accuracy of 82.21%. \n",
    "#We can also see that in Epoch 7 we got accuracy of 83.24 which is better than the final accuarcy.\n",
    "# There are ways to give a checkpoint in between the training and save that version, \n",
    "#we will look at it in subsequent chapters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We are saving the final model as a file here. The model can be then loaded again as and when required.\n",
    "# The model will be saved as a HDF5 file. And it can be reused later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "catDogImageclassifier.save('catdog_cnn_model.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the saved model. The saved file is loaded using load_model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import load_model \n",
    "catDogImageclassifier = load_model('catdog_cnn_model.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check how the model is predicting for an unseen image. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dog\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from keras.preprocessing import image\n",
    "an_image =image.load_img('/Users/vaibhavverdhan/Book Writing/2.jpg',target_size =(64,64))# Load the image\n",
    "# The image is now getting converted to array of numbers\n",
    "an_image =image.img_to_array(an_image)\n",
    "#Let us now expand it's dimensions. It will improve the prediction power \n",
    "an_image =np.expand_dims(an_image, axis =0)\n",
    "# call the predict method here\n",
    "verdict = catDogImageclassifier.predict(an_image)\n",
    "if verdict[0][0] >= 0.5:\n",
    "    prediction = 'dog'\n",
    "else:\n",
    "    prediction = 'cat'\n",
    "# Let us print our final prediction    \n",
    "print(prediction)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Here in this example, we have designed a Neural Network using Kears. We trained using images of Cats and Dogs. \n",
    "#And then tested it.\n",
    "# It is possible to train a multi-classifier system too. The onus lies to get the images for \n",
    "# each of the class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
